﻿/**             © 2008 Avanade Inc. All Rights Reserved.
 * 
 * Authors:     Joris Valkonet joris.valkonet@avanade.com  Avanade Netherlands
 *              Thanh Luc      thanh.luc@avanade.com       Avanade Netherlands
 *              Mark Beerens   mark.beerens@avanade.com    Avanade Netherlands
 * 
 * Content:     This method paints a scatter plot if both selected attributes are continuous
 *                 
 * */

#region [Using]
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
//using System.Diagnostics;
using System.Collections.Generic;
#endregion

namespace Avanade.Datamining.SVMPluginViewer
{
    public partial class SVMViewerUserControl : UserControl
    {
        #region [Parameters]
        //the margins of the scatter plot
        private int marginTop = 150;
        private int marginLeft = 100;
        //the height of the scatter plot
        private int canvasHeight = 500;
        private int canvasWidth = 500;
        #endregion

        #region [Paint]

        /// <summary>
        /// The main paint method
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnPaint(Object sender, PaintEventArgs e)
        {
            //get the graphics
            Graphics g = e.Graphics;

            // Draw the ScatterPlot Axes and Grid
            this.DrawScatterPlotAxesGrid(g);
            
            // Draw the data points in the scatterplot
            this.DrawScatterPlot(g);
        }

        /// <summary>
        /// This method draws the scatter plot
        /// </summary>
        /// <param name="g"></param>
        private void DrawScatterPlot(Graphics g)
        {
            //determine which dataset we should use
            DataTable table;
            if (data.Equals("Trainingdata"))
            {
                table = trainingset;
            }
            else
            {
                table = testingSet;
            }

            int nDataPoints = table.Rows.Count;              // total number of data points
            double x;
            double y;
            string classValue;
            string predictValue;

            string x_axis = comboBox1.Text;
            string y_axis = comboBox2.Text;

            Color penColor;

            try
            {

                //loop through the training set 
                foreach (DataRow myRow in table.Rows)
                {

                    // retrieve the x value
                    x = GetValue(myRow, x_axis + "__scaled");

                    // retrieve the y value
                    y = GetValue(myRow, y_axis + "__scaled");

                    // retrieve the class value
                    classValue = GetStringValue(myRow, "__class");

                    // retrieve the predict value
                    predictValue = GetStringValue(myRow, "__predict");

                    // Draw Symbol in Scatter Plot
                    if (classValue.ToString().Equals(predictValue.ToString()) || !showClassifier)
                    {
                        penColor = Color.Brown;
                        ValueColors.TryGetValue(classValue.ToString(), out penColor);
                        this.DrawSymbol(g, x, y, 1, 1, "circle", penColor, 2, myRow);
                    }
                    else
                    {
                        this.DrawSymbol(g, x, y, 1, 1, "circle", misclassifiedColor, 2, myRow);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);//"An exception occured in the function 'DrawScatterPlot'. This program needs to be terminated.");
            }
        }


        /// <summary>
        /// THis method draws a point in the scatter plot
        /// </summary>
        /// <param name="g">Graphics</param>
        /// <param name="x">x value</param>
        /// <param name="y">y value</param>
        /// <param name="xScale">x scalar</param>
        /// <param name="yScale">y scalar</param>
        /// <param name="symbol">the symbol that should be drawn</param>
        /// <param name="penColor">the color</param>
        /// <param name="penWidth">the with</param>
        /// <param name="myRow">the row for more details</param>
        private void DrawSymbol(Graphics g, double x, double y, double xScale, double yScale, string symbol, Color penColor, int penWidth, DataRow myRow)
        {
            //determine some values needed
            //the range of x andy and the offset
            double xRange = canvasWidth;
            double yRange = canvasHeight;
            int xOffset = this.marginLeft -3;
            int yOffset = this.marginTop -3;
            int symbolSize = 6;

            //the upper corner of the x-values
            double xUpperCorner = (x / xScale) * xRange + xOffset;
            double yUpperCorner = yRange * (1 - (y / yScale)) + yOffset;
            
            Pen myPen = new Pen(penColor);
            myPen.Width = penWidth;

            //determine which symbol we should use.. and draw the point
            switch (symbol)
            {
                case "circle":
                    g.FillEllipse(myPen.Brush, (int)xUpperCorner, (int)yUpperCorner, symbolSize, symbolSize);
                    break;
                case "square":
                    g.DrawRectangle(myPen, (int)xUpperCorner, (int)yUpperCorner, symbolSize, symbolSize);
                    break;
            }
        
            // Store the coordinates of the datapoint in the datatable 'traiiningset'.
            // This position is needed for showing the ToolTip of the datapoint.
            //TODO!!!!!
            //this.StoreDataPointCoordinates(myRow, xUpperCorner, yUpperCorner);

        }


        // 
        // 
        /// <summary>
        /// Retrieve the value (i.e. float) from a datarow for a given column name
        /// This function handles the case when the value in the datarow is empty, then numerical value -99999999 is returned.
        /// </summary>
        /// <param name="myRow"></param>
        /// <param name="ColumnName"></param>
        /// <returns></returns>
        private double GetValue(DataRow myRow, string ColumnName)
        {
            double retValue = -99999999;           // dummy value;
 
            // retrieve value
            if (myRow[ColumnName].ToString() != "")     // when the value is not an empty string convert this value to a numerical value.
            {
                retValue = Convert.ToDouble(myRow[ColumnName].ToString());
            }

            return retValue;
        }


        // 
        // 
        /// <summary>
        /// Retrieve the value (i.e. float) from a datarow for a given column name
        /// This function handles the case when the value in the datarow is empty, then the value "" is returned.
        /// </summary>
        /// <param name="myRow"></param>
        /// <param name="ColumnName"></param>
        /// <returns></returns>
        private string GetStringValue(DataRow myRow, string ColumnName)
        {
            String retValue = "";           // dummy value;

            // retrieve value
            if (myRow[ColumnName].ToString() != "")     // when the value is not an empty string convert 
            {
                retValue = myRow[ColumnName].ToString();
            }

            return retValue;
        }

        /// <summary>
        /// This method sets a value on a column
        /// </summary>
        /// <param name="myRow"></param>
        /// <param name="ColumnName"></param>
        /// <param name="value"></param>
        private void SetValue(DataRow myRow, string ColumnName, int value)
        {
            myRow[ColumnName] = value;
            myRow.AcceptChanges();
        }

        #endregion

        #region [Draw Axes]

        /// <summary>
        /// Draw the axis and border of the scatter plot
        /// </summary>
        /// <param name="g"></param>
        private void DrawScatterPlotAxesGrid(Graphics g)
        {
            // Draw the rectangle that is the scatter plot     
            g.DrawRectangle(new Pen(Color.White), marginLeft, marginTop, canvasWidth, canvasHeight);

            //do we need to draw the axes?
            if (comboBox4.Text == "Yes")
            {
                // Draw horizontal lines              
                for (int i = 0; i <= 500; i += 50)
                {
                    g.DrawLine(new Pen(Color.Black), new Point(marginLeft, marginTop + i), new Point(marginLeft + canvasWidth, marginTop + i));
                }

                // Draw horizontal lines              
                for (int i = 0; i <= 500; i += 50)
                {
                    g.DrawLine(new Pen(Color.Black), new Point(marginLeft + i, marginTop), new Point(marginLeft + i, marginTop + canvasHeight));
                }
            }
            // Set text font
            Font font = new Font("Arial", 10);

            // Write X values
            double nGrid_X = 10.0;
            double x_scale = (this.maxX - this.minX) / nGrid_X;
            string strX;
            double xPos;
            for (int i = 0; i <= nGrid_X; i++)
            {
                strX = String.Format("{0:#.##}", this.minX + i * x_scale);
                xPos = this.marginLeft + (canvasWidth / nGrid_X) * i;
                g.DrawString(strX, font, new SolidBrush(Color.Black), (int)xPos, marginTop + canvasHeight + 10);

            }

            // Write Y values
            double nGrid_Y = 10.0;
            double y_scale = (this.maxY - this.minY) / nGrid_Y;
            string strY;
            double yPos;
            for (int i = 0; i <= nGrid_Y; i++)
            {
                strY = String.Format("{0:#.##}", this.minY + i * y_scale);
                yPos = this.marginTop + canvasHeight - (canvasHeight / nGrid_Y) * i;
                g.DrawString(strY, font, new SolidBrush(Color.Black), marginLeft - 35, (int)yPos);

            }
        }
        #endregion
    }
}
